# TurboWiki is a simple wiki module for TurboGears
#
# Copyright (C) 2009 Alberto Granzotto <agranzot@gmail.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
# 
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
# 
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.

import pkg_resources
pkg_resources.require("SQLAlchemy>=0.3.10")

import os

from turbogears.database import metadata, mapper
from sqlalchemy import Table, Column, ForeignKey
from sqlalchemy.orm import relation, backref
from sqlalchemy import String, Unicode, Integer, DateTime,\
        Date, UnicodeText, Boolean, Float, func, select, desc
from sqlalchemy.orm import synonym
from datetime import datetime, date
import turbogears as tg

from utils import get_locale, wiki_parser, get_user_name, get_user_name_url

wiki_upload_url = tg.config.get('turbowiki.upload.url', 'wiki_uploads')
wiki_upload_dir = tg.config.get('turbowiki.upload.dir',
    os.path.join(os.getcwd(), tg.util.get_package_name(),
        'static', wiki_upload_url))

try:
    os.mkdir(wiki_upload_dir)
except:
    pass

class Page(object):
    '''Page is an interface to a content. A Page contains
    the history of the content and can contain also the same content
    but different languages.

    Content is a container for various metadata (see Content class
    definition for more infos) and, obviously, for the text.

    If you want to retrieve a specific content you have to set
    one or all of attributes 'lang' and 'rev'. After that all attributes
    will be updated to the correct values. There is also the 'get_content'
    method to do this.
    '''

    lang = None
    rev = None

    @classmethod
    def by_name(cls, name):
        '''Retrieve a page by name (which is unique)
            name: the name
        '''
        p = cls.query.filter_by(name=name).first()
        return p

    @classmethod
    def failsafe_by_name(cls, name):
        '''Retrieve a page by name (which is unique). If the page
        does not exist an empty page is returned.
            name: the name
        '''
        p = cls.query.filter_by(name=name).first()
        if not p:
            p = Page(name=name)
        return p

    @property
    def title(self):
        c = self.content
        if c:
            return self.content.title
        return None

    @property
    def text(self):
        c = self.content
        if c:
            return self.content.text
        return None

    @property
    def formatted_text(self):
        text = self.text
        if text:
            return wiki_parser(self.text)
        else:
            return ''
            #return _('The page "%s" does not exist.' % self.name)

    @property
    def lang(self):
        c = self.content
        if c:
            return self.content.lang
        return None

    @property
    def rev(self):
        c = self.content
        if c:
            return self.content.rev
        return None

    @property
    def content(self):
        return self.get_content()

    def get_content(self, lang=None, rev=None):
        '''Retrieve the content for the specified lang and rev.'''
        if not lang:
            lang = get_locale()
        content = self._get_content(lang, rev)
        if not content:
            default_lang = tg.config.get(
                'turbowiki.i18n.default', 'en')
            content = self._get_content(lang=default_lang, rev=rev)
        if not content:
            content = self._get_content(lang=None, rev=rev)
        return content

    def _get_content(self, lang=None, rev=None):
        '''Retrieve the content for the specified lang and rev.'''
        q = Content.query()
        q = q.filter(Content.page_id == self.id)
        q = q.order_by(Content.lang.desc())
        q = q.order_by(Content.created.desc())
        q = q.order_by(Content.rev.desc())
        if lang:
            q = q.filter(Content.lang == lang)
        if rev:
            q = q.filter(Content.rev == rev)
        return q.first()

    @property
    def last_edit(self):
        return self.content.created

    def append(self, v):
        self.contents.append(v)

    @property
    def history(self):
        q = Content.query()
        q = q.filter(Content.page_id == self.id)
        q = q.order_by(Content.created.desc())
        q = q.order_by(Content.rev.desc())
        q = q.order_by(Content.lang.desc())
        return q.all()

class Content(object):
    @property
    def user(self):
        if self.user_id:
            return get_user_name(self.user_id)
        else:
            if self.ip_address.startswith('::ffff:'):
                return self.ip_address[7:]
            return self.ip_address

    @property
    def user_url(self):
        if self.user_id:
            return get_user_name_url(self.user_id)
        return ''

    @property
    def pretty_created(self):
        return tg.i18n.format.format_date(
            self.created, format='short', time_format=' - %H:%M')

    @property
    def formatted_text(self):
        text = self.text
        if text:
            return wiki_parser(self.text)
        else:
            return ''
            #return _('The page "%s" does not exist.' % self.name)

class WikiFile(object):
    def __init__(self, wiki_name, wiki_file):
        if not wiki_file.filename:
            return
        file_type = wiki_file.filename.split('.')[-1]
        file_name = os.path.join(
            wiki_upload_dir, '%s.%s' % (wiki_name, file_type))
        # NOT thread safe
        f = file(file_name, 'wb')
        f.write(wiki_file.file.read())
        
    @classmethod
    def exists(cls, wiki_name):
        file_list = map(lambda f: os.path.splitext(f)[0], cls.all())
        for f in file_list:
            if wiki_name == f:
                return True
        return False

    @classmethod
    def all(cls):
        return os.listdir(wiki_upload_dir)

page_table = Table(
    'wiki_page', metadata,
    Column('id', Integer, primary_key=True),
    Column('name', String(256), unique=True)
)

content_table = Table(
    'wiki_content', metadata,
    Column('id', Integer, primary_key=True),
    Column('page_id', Integer,
            ForeignKey('wiki_page.id', ondelete="CASCADE")),
    Column('created', DateTime, default=datetime.now),
    Column('rev', Integer, nullable=False),
    Column('lang', String(5), nullable=False),
    Column('title', Unicode(256)),
    Column('text', UnicodeText),
    Column('user_id', Integer),
    Column('ip_address',String(39), default="::ffff:127.0.0.1")
)

################
# wiki mapping #
################

# Page 1:n Content
mapper(Page, page_table, properties=dict(
    contents = relation(Content,
        backref='page',
        cascade='all, delete, delete-orphan')
    ),
    save_on_init=False
)
mapper(Content, content_table,
    save_on_init=False
)
